import React, { useState, useRef, useMemo, useEffect } from 'react';
import { Select, Spin } from 'antd';
import debounce from 'lodash/debounce';
import type { ValueTextType } from '@/constants';
import isFunction from 'lodash/isFunction';

type Props = {
  fetchOptions: (...restParams: any[]) => Promise<{ label: any; value: any }[]>;
  debounceTimeout: number;
  formatPropsValue?: (value: any) => any;
  formatFetchOptionsParams?: (inputValue: string, ctx: any) => any[];
  formatOptions?: (data: any, ctx: any) => any[];
  [key: string]: any;
};
type SelectOptions = ValueTextType & { label: string };
const { Option } = Select;

const DebounceSelect: React.FC<Props> = ({
  fetchOptions,
  debounceTimeout = 500,
  formatPropsValue,
  formatFetchOptionsParams,
  formatOptions,
  ...restProps
}) => {
  const props = { ...restProps };
  const { ctx, filterOption } = props;
  if (isFunction(formatPropsValue)) {
    props.value = formatPropsValue(props.value);
  }
  const [fetching, setFetching] = useState(false);
  const [options, setOptions] = useState(props.options || props.source || []);
  useEffect(() => {
    setOptions(props.source || []);
  }, [props.source]);

  const fetchRef = useRef(0);
  const debounceFetcher = useMemo(() => {
    const loadOptions = (value: string) => {
      if (!value) return;
      fetchRef.current += 1;
      const fetchId = fetchRef.current;
      setOptions([]);
      setFetching(true);
      const fetchParams = formatFetchOptionsParams ? formatFetchOptionsParams(value, ctx) : [value];
      // eslint-disable-next-line prefer-spread
      fetchOptions.apply(null, fetchParams).then((newOptions) => {
        if (fetchId !== fetchRef.current) {
          return;
        }
        let finalOptions = newOptions;
        if (formatOptions && isFunction(formatOptions)) {
          finalOptions = formatOptions(newOptions, ctx);
        }
        finalOptions =
          filterOption && Array.isArray(finalOptions)
            ? filterOption?.(finalOptions, ctx)
            : finalOptions;
        setOptions(finalOptions);
        setFetching(false);
      });
    };

    return debounce(loadOptions, debounceTimeout);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [fetchOptions, debounceTimeout]);

  return (
    <Select
      showSearch
      allowClear
      onSearch={debounceFetcher}
      {...props}
      filterOption={false} // 保持对props中filterOption属性的复写，不可变更位置
      notFoundContent={fetching ? <Spin size="small" /> : null}
    >
      {options.map((option: SelectOptions) => (
        <Option value={option.value} key={option.value}>
          {option.text || option.label}
        </Option>
      ))}
    </Select>
  );
};
export default DebounceSelect;
